'''
这是一个线性空间的元类
'''
from 数域 import Field,Q,R,C,F
import copy
import pdb
class LinearSpace(type):
    pool={}
    # 可复用


    def __new__(cls,name,field=F):
        
        if name in LinearSpace.pool:
            return LinearSpace.pool[name]
        
        def nw(cls2,value):
            if value in cls2.pool:
                return cls2.pool[value]
            ne=object.__new__(cls2)
            ne.value=value
            ne.op=None#
            ne.out=pre(value)
            cls2.pool[value]=ne
            cls2.eqSet.append({value})
            return ne
        def init(self,value):
            pass
        
        def has(self):
            return hash(self.value)

        @staticmethod
        def pre(tree):
            if isinstance(tree,tuple):
                # tree[0]=='+' or tree[0]=='*':
                return '('+tree[0].join(pre(i) for i in tree[1:])+')'
            return str(tree)

        def rep(self):
            return self.out
        st=rep

        def fce(self,other):
            if isinstance(other,new):
                ts,to=-1,-1
                for i in range(len(self.eqSet)):
                    if self.value in self.eqSet[i]:
                        ts=i
                    if other.value in self.eqSet[i]:
                        to=i
                    if ts!=-1 and to!=-1:
                        break
                if not ts==to:
                    self.eqSet[ts]=self.eqSet[ts]|self.eqSet[to]
                    del self.eqSet[to]
                return
            return NotImplemented
        def eq(self,other):
            if isinstance(other,new):
                for i in self.eqSet:
                    if self.value in i and other.value in i:
                        return True
                if self.op!=None and self.op==other.op:
                    self.fce(other)
                    return True
                return False# 应理解为未知
            return False# 为避免op不同类型引发错误
        
        def ad(self,other):
            ne=new(('+',self.value,other.value))
            ne.op=('+',self,other)
            return ne
        def add(self,other):
            if isinstance(other,new):
                ne=self.ad(other)
                if isinstance(self.op,tuple) and self.op[0]=='+':
                    t=self.op[1].ad(self.op[2].ad(other))
                    ne.fce(t)
                if isinstance(other.op,tuple) and other.op[0]=='+':
                    t=self.ad(other.op[1]).ad(other.op[2])
                    ne.fce(t)
##                pdb.set_trace()
                if isinstance(self.op,tuple) and self.op[0]=='*':
                    c1,v1=self.op[1],self.op[2]
                else:
                    c1,v1=new.field(1),self
                if isinstance(other.op,tuple) and other.op[0]=='*':
                    c2,v2=other.op[1],other.op[2]
                else:
                    c2,v2=new.field(1),other
                if v1==v2:
                    t=v1.rm(c1+c2)
                    ne.fce(t)
                    ne.out=t.out
                return ne
            return NotImplemented

        def rm(self,other):
            ne=new(('*',other.value,self.value))
            ne.op=('*',other,self)
            return ne
        def rmul(self,other):
            if isinstance(other,new.field):
                ne=self.rm(other)
                if other==new.field(1):
                    ne.fce(self)
                    ne.out=self.out
                if isinstance(self.op,tuple) and self.op[0]=='+':
                    t=self.op[1].rm(other)+self.op[2].rm(other)
                    ne.fce(t)
                if isinstance(self.op,tuple) and self.op[0]=='*':
                    t=self.op[2].rm(other*self.op[1])
                    ne.fce(t)
                    if t.op[1]==new.field(1):
                        ne.fce(t.op[2])
                        ne.out=t.op[2].out
                return ne
            return NotImplemented

    
        attrs={
            'name':name,
            'field':field,
            'pool':{},
            'eqSet':[],
            '__new__':nw,
            '__init__':init,
            '__hash__':has,
            '__repr__':rep,
            '__str__':st,
            '__add__':add,
            'ad':ad,
            'add':add,
            '__rmul__':rmul,
            'rm':rm,
            'fce':fce,
            '__eq__':eq
            }
        new=super().__new__(cls,name,(),attrs)
        
        LinearSpace.pool[name]=new
        
        def eadd(self,other):
            if isinstance(other,new):
                ne=new.add(self,other)
                ne.fce(other)
                ne.out=other.out
                return ne
            return NotImplemented
        ze=super().__new__(cls,name+'_zero',(new,),{'__add__':eadd})
        ze('o')
        
        def nw(cls2,vec,zero=ze('o'),name=''):
            value=f'{vec}_{zero}'+name
            a=new.__new__(cls2,value)
            a.zero=zero
            a.vec=vec
            return a
        def init(cls2,vec,zero=ze('o'),name=''):
            pass
        def nad(self,other):
            ne=ze(('+',self.value,other.value))
            ne.op=('+',self,other)
            return ne
        def nadd(self,other):
            if isinstance(other,new):
                if other==self.vec:
                    ne=self.ad(other)
                    ne.fce(self.zero)
                    ne.out=self.zero.out
                    if isinstance(self.op,tuple) and self.op[0]=='+':
                        t=self.op[1].ad(self.op[2].ad(other))
                        ne.fce(t)
                    if isinstance(other.op,tuple) and other.op[0]=='+':
                        t=self.ad(other.op[1]).ad(other.op[2])
                        ne.fce(t)
                    if isinstance(self.op,tuple) and self.op[0]=='*':
                        c1,v1=self.op[1],self.op[2]
                    else:
                        c1,v1=new.field(1),self
                    if isinstance(other.op,tuple) and other.op[0]=='*':
                        c2,v2=other.op[1],other.op[2]
                    else:
                        c2,v2=new.field(1),other
                    if v1==v2:
                        t=v1.rm(c1+c2)
                        ne.fce(t)
                else:
                    ne=new.add(self,other)
                return ne
            return NotImplemented
        neg=super().__new__(cls,name+'_neg',(new,),{'__new__':nw,'__init__':init,'__add__':nadd,'ad':nad})

        LinearSpace.pool[name+'_zero']=ze
        LinearSpace.pool[name+'_neg']=neg
        
        return new

    def __init__(cls,name,field=F):
        pass
    
if __name__=='__main__':
    V=LinearSpace('V',Q)
    Vz=LinearSpace('V_zero')
    Vn=LinearSpace('V_neg')
    a,b,c=V('a'),V('b'),V('c')
    o=V('o')
    d=Vn(a)
